-----------------------------------------------------------------------------
-- |
-- Module      : LLVM.Parser.Primitives
-- Copyright   : Mikhail Belyaev
-- License     : GPLv3 (see LICENSE)
--
--
-- This is a BitGet-based realization for parsing LLVM record elements.
--
-- TODO: tests
-----------------------------------------------------------------------------

module LLVM.Parser.Bits.Struct where

import LLVM.Parser.Bits.BitGet
import LLVM.Parser.Bits.Primitives
import LLVM.Util

import Control.Monad
import Control.Monad.Error (throwError)

-- Type representing some piece of data in bitstream format
data FieldVal =
        IntVal Integer |      -- FBR/VBR integer (IntVal <value>)
        CharVal Char   |      -- Sixbit char (CharVal <value>)
        Void           |      -- Auxilliary type repr. anything without a value
        Many [FieldVal]       -- A strem of unknown-sized objects
        deriving (Show,Eq)

-- Getting an integer value from a field, if any
integerValueOf :: FieldVal -> Maybe Integer
integerValueOf (IntVal v) = Just v
integerValueOf _ = Nothing

charValueOf :: FieldVal -> Maybe Char
charValueOf (CharVal c) = Just c
charValueOf _ = Nothing

listValueOf :: FieldVal -> Maybe [FieldVal]
listValueOf (Many fvs) = Just fvs
listValueOf _ = Nothing

-- Integral structural primitive
data IntegralPrimitive =
        FBR Int | -- fixed (bitsize)
        VBR Int   -- variable (bitsize)
        deriving Show
-- Structure Primitive
data StructPrimitive =
        Integral IntegralPrimitive | -- integral primitive
        Char6                      | -- char primitive
        Align32                      -- align bits
        deriving Show
-- Structure element
data StructElem =
        Prim StructPrimitive                          | -- a primitive elem
        Arr IntegralPrimitive StructElem              | -- an elem array
        Comp [StructElem]                             | -- a composite elem
        Variable StructElem (FieldVal -> MayError StructElem)  | -- a flexible elem
        Const FieldVal                                | -- a constant
        VoidElem                                        -- a dummy

-- the only problem with show is use of functions
instance Show StructElem where
        show (Prim a) = "Prim " ++ show a
        show (Arr a b) = "Arr " ++ show a ++ " " ++ show b
        show (Comp a) = "Comp " ++ show a
        show (Variable a _) = "Variable " ++ show a ++ " binded"
        show (Const b) = "Const " ++ show b
        show (VoidElem) = "VoidElem"

-- More verbose definition:
-- Prim StructPrimitive - obvious
--     !Reads to corresponding value
--            Integral -> IntVal
--            Char6 -> CharVal
--            Align32 -> Void
-- Arr size elem - a dynamic array is a structure consisting of
--     integral primitive defining size (N) and N elems of given structure
--     is distinct 'cos we don't know N before it is read.
--     !Reads to Many (does not include size elem)
-- Comp elems - a simple composition of known maybe-different elems.
--     is useful as a piece in other complicated elem types
--     !Reads to Many
-- Variable elem dependant - a variative element is a pair of a key element
--     (elem) and a binding used to find out the next one. Useful for tricky
--     dynamically-known stuff. Note that dependent element can be of any kind,
--     so several elems can be read through using Comp or Arr as return.
--     !Reads to Many
-- Const elem value - more of an assertion thingie, reads elem and compares it
--     with value. Returns the value on match, fails otherwise.
--     !Reads to value's type
-- VoidElem doesn't read anything and is used for convention (!Reads to Void)

-- getters for primitives
getPField :: StructPrimitive -> ErrBitGet FieldVal
getPField (Integral (FBR smth)) = do
        ret <- getFBR smth
        return $ IntVal ret
getPField (Integral (VBR smth)) = do
        ret <- getVBR smth
        return $ IntVal ret
getPField Char6 = do
        ret <- getSixBitChar
        return $ CharVal ret
getPField Align32 = do
        getAlign32
        return Void

-- getters for elems
getField :: StructElem -> ErrBitGet FieldVal
getField (Prim pr) = getPField pr
getField (Arr sz el) =   do
                                size <- getPField (Integral sz)
                                sval <- promoteMaybe (integerValueOf size) "Structural error in parser"
                                args <- getField el `loop` (fromInteger sval)
                                return $ Many args -- :)
getField (Comp [smth]) = let decomp = liftM Many
                         in decomp $ getFields [smth]
getField (Variable key bindf) =
                              do
                                 resKey <- getField key
                                 resKey'' <- promoteError $ bindf resKey
                                 resVal <- getField resKey''
                                 -- return resVal
                                 return $ Many [resKey,resVal]

getField (Const val) = return val
getField VoidElem = return Void

-- get some fields at once
getFields :: [StructElem] -> ErrBitGet [FieldVal]
getFields = mapM getField
